﻿using System;
using System.Threading;

namespace Unity.Commons.Client.Threading
{
    public sealed class AtomicInteger : IEquatable<AtomicInteger>, IEquatable<int>, IFormattable
    {

        private int _value;

        public AtomicInteger() : this(0) { }

        public AtomicInteger(int value)
        {
            _value = value;
        }

        public int Value
        {
            get => Interlocked.CompareExchange(ref _value, 0, 0);
            set => Interlocked.Exchange(ref _value, value);
        }

        public int GetAndSet(int newValue)
        {
            return Interlocked.Exchange(ref _value, newValue);
        }

        public bool CompareAndSet(int expect, int update)
        {
            int result = Interlocked.CompareExchange(ref _value, update, expect);
            return result == expect;
        }

        public int GetAndIncrement()
        {
            return Interlocked.Increment(ref _value) - 1;
        }

        public int GetAndDecrement()
        {
            return Interlocked.Decrement(ref _value) + 1;
        }

        public int GetAndAdd(int value)
        {
            return Interlocked.Add(ref _value, value) - value;
        }

        public int IncrementAndGet()
        {
            return Interlocked.Increment(ref _value);
        }

        public int DecrementAndGet()
        {
            return Interlocked.Decrement(ref _value);
        }

        public int AddAndGet(int value)
        {
            return Interlocked.Add(ref _value, value);
        }

        public bool Equals(AtomicInteger other)
        {
            if (other is null)
            {
                return false;
            }
            return Value == other.Value;
        }

        public bool Equals(int other)
        {
            return Value == other;
        }

        public override bool Equals(object obj)
        {
            if (obj is AtomicInteger atomicValue)
            {
                return Equals(atomicValue);
            }
            if (obj is int intValue)
            {
                return Equals(intValue);
            }
            return false;
        }

        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }

        public override string ToString()
        {
            return _value.ToString();
        }

        public string ToString(string format)
        {
            return _value.ToString(format);
        }

        public string ToString(IFormatProvider provider)
        {
            return _value.ToString(provider);
        }

        public string ToString(string format, IFormatProvider formatProvider)
        {
            return _value.ToString(format, formatProvider);
        }

        public static implicit operator int(AtomicInteger atomicObject)
        {
            return atomicObject.Value;
        }

        public static bool operator ==(AtomicInteger thisObject, AtomicInteger thatObject)
        {
            return thisObject.Value == thatObject.Value;
        }

        public static bool operator !=(AtomicInteger thisObject, AtomicInteger thatObject)
        {
            return !(thisObject == thatObject);
        }

        public static bool operator ==(AtomicInteger atomicObject, int intValue)
        {
            return atomicObject.Value == intValue;
        }

        public static bool operator !=(AtomicInteger atomicObject, int intValue)
        {
            return !(atomicObject == intValue);
        }

        public static bool operator ==(int intValue, AtomicInteger atomicObject)
        {
            return intValue == atomicObject.Value;
        }

        public static bool operator !=(int intValue, AtomicInteger atomicObject)
        {
            return !(intValue == atomicObject);
        }

        public static bool operator ==(AtomicInteger atomicObject, int? nullableIntValue)
        {
            return atomicObject.Value == nullableIntValue.GetValueOrDefault();
        }

        public static bool operator !=(AtomicInteger atomicObject, int? nullableIntValue)
        {
            return !(atomicObject == nullableIntValue);
        }

        public static bool operator ==(int? nullableIntValue, AtomicInteger atomicObject)
        {
            
            return nullableIntValue.GetValueOrDefault() == atomicObject.Value;
        }

        public static bool operator !=(int? nullableIntValue, AtomicInteger atomicObject)
        {
            return !(nullableIntValue == atomicObject);
        }

    }
}
